import config
from load_log import LoadLog
import os
from collections import namedtuple
import re

TargetAttribute =  namedtuple('TargetAttribute', ['projectName', 'targetName', 'targetType', 'macro', 'includeDirs', 'compileOptsCC', 'compileOptsCXX', 'srcFiles', 'linkLibs'])

class ParseTargetBuildArg():
    '''
        解析每一个目标的构建过程
    '''
    def __init__(self, projectName, logLines):
        self._projectName = projectName
        self._targetLogs = logLines
        self._macro = []
        self._includeDirs = []
        self._compileOptsCC = []
        self._compileOptsCXX = []
        self._srcFiles = []
        self._objCount = 0
        self._linklibs = []
        self._targetName = ""
        self._type = ""
        self._compileTool = ""
        self._linkTool = ""
        # print(len(self._targetLogs))

    def __repr__(self):
        return '''_targetName:{}
_type:{}
_macro:{}
_includeDirs:{}
_compileOptsCC:{}
_compileOptsCXX:{}
_srcFiles:{}
_linklibs:{}'''.format(self._targetName, self._type, self._macro, self._includeDirs, self._compileOptsCC, self._compileOptsCXX, self._srcFiles, self._linklibs)

    def SliceLink(self):
        '''以Link为界限分割编译过程和连接过程'''
        self._linkLine = [self._targetLogs.index(line) for line in self._targetLogs if line.find(config.LINK_TARGET) > 0]
        # if len(self._linkLine) != 1:
        #     print("Slice Link line ERROR!")
        #     exit()
        return len(self._linkLine)
    
    def FindCompileLog(self):
        '''找到编译的日志'''
        self._compileLog = [log for log in self._targetLogs[ : self._linkLine[0]] if log.find(config.COMPILER_CXX) != -1
                                                                                  or log.find(config.COMPILER_CC) != -1]

    def __RemoveLogCompiler(self, log):
        '''移除日志前的编译器'''
        # log.find(config.COMPILER_CXX) if log.find(config.COMPILER_CXX) != -1 else log.find(config.COMPILER_CC) if log.find(config.COMPILER_CC) != -1 else log.find(config.COMPILER_AR)
        '''compiler 经过FindxxxLog后只保留了三种编译器的log'''
        if log.find(config.COMPILER_CXX) != -1:
            # print(config.COMPILER_CXX)
            compilerIndex = log.find(config.COMPILER_CXX)
            compilerStrLen = len(config.COMPILER_CXX)
            self._compileTool = config.COMPILER_CXX
            self._linkTool = config.COMPILER_CXX
        elif log.find(config.COMPILER_CC) != -1:
            # print(config.COMPILER_CC)
            compilerIndex = log.find(config.COMPILER_CC)
            compilerStrLen = len(config.COMPILER_CC)
            self._compileTool = config.COMPILER_CC
            self._linkTool = config.COMPILER_CC
        else:
            # print(config.COMPILER_AR)
            compilerIndex = log.find(config.COMPILER_AR)
            compilerStrLen = len(config.COMPILER_AR)
            self._linkTool = config.COMPILER_AR
        return log[compilerIndex + compilerStrLen : ]

    def CollectTargetName(self):
        targetLog = self._targetLogs[-1]
        index = targetLog.find(config.SLICE_TARET)
        self._targetName = os.path.split(targetLog[index : ][len(config.SLICE_TARET) : ])[-1].rstrip()

    def CollectTargetType(self):
        linkLine = self._targetLogs[self._linkLine[0]]
        for tType in config.TARGET_TYPE:
            if linkLine.find(tType) != -1:
                self._type = tType
                break

    def _CollectMacro(self, arg):
        if arg.startswith("-D"):
            macroName = arg[2 : ]
            if macroName not in self._macro:
                self._macro.append(macroName)
            return True
        return False

    def _CollectIncludeDir(self, arg):
        if arg.startswith("-I"):
            dirIndex = len(config.WorkingDir)
            incDir = arg[2 : ][dirIndex : ]
            if incDir not in self._includeDirs:
                self._includeDirs.append(incDir)
            return True
        return False

    def _CollectIncludeDir2(self, arg):
        if arg == "-isystem":
            return True
        return False

    def _CollectCompleOpts(self, arg):
        if self._compileTool == config.COMPILER_CXX:
            if arg not in self._compileOptsCXX:
                self._compileOptsCXX.append(arg)
        else:
            if arg not in self._compileOptsCC:
                self._compileOptsCC.append(arg)

    def _CollectSrcFiles(self, arg):
        dirIndex = len(config.WorkingDir)
        srcPath = arg[dirIndex : ]
        if srcPath not in self._srcFiles:
            self._srcFiles.append(srcPath)
        return True

    def _BypassCompileArg(self, arg):
        if arg.startswith('--sysroot='):
            return 1
        if arg in config.COMLILE_BLACK_OPTION0:
            # print(arg)
            return 1
        if arg in config.COMLILE_BLACK_OPTION1:
            # 跳过后面的选项
            return 2
        if arg.endswith('.o"'):
            return 1
        return 0

    def ParseCompile(self):
        '''解析编译日志, 提取出宏定义, include路径, 编译选项, 源文件'''
        for log in self._compileLog[ : ]:
            # print(log)
            buildArg = self.__RemoveLogCompiler(log)
            argList = buildArg.split()
            i = 0
            while i < len(argList):
                stepJump = self._BypassCompileArg(argList[i])
                if stepJump != 0:
                    i += stepJump
                    continue
                if self._CollectMacro(argList[i]):
                    i += 1
                    continue
                if self._CollectIncludeDir(argList[i]):
                    i += 1
                    continue
                if self._CollectIncludeDir2(argList[i]):
                    dirIndex = len(config.WorkingDir)
                    incDir = argList[i + 1][dirIndex : ]
                    if incDir not in self._includeDirs:
                        self._includeDirs.append(incDir)
                    i += 2
                    continue
                if argList[i] == "-c": # 源文件跟在其后
                    self._CollectSrcFiles(argList[i+1])
                    i += 2
                    continue
                self._CollectCompleOpts(argList[i])
                i += 1
            # print(argList)
    
    def FindLinkLog(self):
        '''找到连接的日志'''
        self._linkLog = [log for log in self._targetLogs[self._linkLine[0] : ] if log.find(config.COMPILER_CXX) != -1 
                                                                            or log.find(config.COMPILER_CC) != -1
                                                                            or log.find(config.COMPILER_AR) != -1]
    def _BypassLinkArg(self, arg):
        if arg.startswith('--sysroot='):
            return 1
        if arg in config.LINK_BLACK_OPTION0:
            return 1
        if arg in config.LINK_BLACK_OPTION1:
            return 2
        if arg.startswith('-Wl,'):
            return 1
        if arg.endswith('.o'):
            return 1
        if arg.endswith('.o"'):
            return 1
        return 0

    def ParseLink(self):
        '''解析链接时的参数'''
        # print(self._linkLog)
        # 链接日志只有1条
        log = self._linkLog[0]
        linkArg = self.__RemoveLogCompiler(log).rstrip()
        argList = linkArg.split()
        i = 0
        # print(self._linkTool)
        while i < len(argList):
            if self._linkTool != config.COMPILER_AR:
                # print(argList[i])
                jumpStep = self._BypassLinkArg(argList[i])
                if jumpStep != 0:
                    i += jumpStep
                    continue
                if argList[i].startswith('-l'):
                    libName = argList[i][2 : ]
                    if libName not in self._linklibs:
                        self._linklibs.append(libName)
                    i += 1
                    continue
                if argList[i].endswith('.a'):
                    linklib = os.path.split(argList[i])[-1].rstrip()
                    # print(linklib)
                    libName = linklib[3 : ][ : -2]
                    if libName not in self._linklibs:
                        self._linklibs.append(':' + libName)
                    i += 1
                    continue
                # ../libcjson.so.1.7.15 *lib*.so*
                pattern= re.compile(r'.*lib(.*).so.*')
                regRet = pattern.search(argList[i])
                if regRet:
                    if regRet.group(1) not in self._linklibs:
                        self._linklibs.append(':' + regRet.group(1))
                    i += 1
                    continue
                self._CollectCompleOpts(argList[i])
                i += 1
            else :
                i += 1

    def ParseTargetBuildPara(self):
        if self.SliceLink() == 0:
            return None
        self.CollectTargetName()
        self.CollectTargetType()
        self.FindCompileLog()
        self.ParseCompile()
        self.FindLinkLog()
        self.ParseLink()
        # 返回元组 project name, target name, target type, macro, include dirs, compile opts, src files, link libs
        return TargetAttribute(self._projectName, self._targetName, self._type, self._macro, self._includeDirs, self._compileOptsCC, self._compileOptsCXX, self._srcFiles, self._linklibs)

if __name__ == "__main__":
    loader = LoadLog('./libtiff.log')
    lineBlocks = loader.GetTargetLineBlock()
    projectName = lineBlocks[2]
    targetBuildLog = lineBlocks[1][lineBlocks[0][0][0] : lineBlocks[0][0][1]]
    ptb = ParseTargetBuildArg(projectName, targetBuildLog)

    ret = ptb.ParseTargetBuildPara()
    print(ret)
